package util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IList;
import com.hazelcast.core.IMap;
import com.hazelcast.core.ISemaphore;

@Configuration
@EnableCaching
@ComponentScan
public class SpringCache {
	private final static Logger logger = LoggerFactory.getLogger(SpringCache.class);

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		@SuppressWarnings("unused")
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringCache.class);
		// To test cache feature
		// MapService bean = ctx.getBean(MapService.class);
		// while (true) {
		// System.out.println(bean.read("1"));
		// try {
		// Thread.sleep(1000);
		// } catch (InterruptedException e) {
		// e.printStackTrace();
		// }
		// }
	}

	@Bean
	public HazelcastInstance hz() {
		return HazelcastClient.newHazelcastClient();
	}

	@Bean
	public IMap<String, String> map(HazelcastInstance hz) {
		return hz.getMap(NearCacheTest.MAP);
	}

	@Bean
	public SimpleCacheManager cacheManager() {
		SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
		ConcurrentMapCache concurrentMapCache = new ConcurrentMapCache(NearCacheTest.MAP);
		Collection<ConcurrentMapCache> caches = new ArrayList<>();
		caches.add(concurrentMapCache);
		simpleCacheManager.setCaches(caches);
		return simpleCacheManager;
	}

	@PostConstruct
	public void init() {
		// start reading
		Random random = new Random(System.currentTimeMillis());
		new Thread(reader, "cache-reader" + random.nextInt()).start();
		new Thread(evict, "cache-evict" + random.nextInt()).start();
	}

	@Autowired
	private MapService mapService;

	@Autowired
	private HazelcastInstance hz;

	private Runnable reader = new Runnable() {
		public void run() {
			while (true) {
				logger.info("testmap(1):{}, testmap(2):{}", mapService.read("1"), mapService.read("2"));
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	};

	private Runnable evict = new Runnable() {
		public void run() {
			while (true) {
				try {
					ISemaphore evict = hz.getSemaphore(NearCacheTest.SEMAPHORE_EVICT);

					logger.info("waiting for evict event.");
					evict.acquire();
					logger.info("evict all change");
					// evict all change
					IList<String> evictList = hz.getList(NearCacheTest.SEMAPHORE_EVICT);
					for (Iterator<String> iterator = evictList.iterator(); iterator.hasNext();) {
						mapService.evict(iterator.next());
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	};

}
